import os
import fnmatch
import pickle
import string
import sys

from kivy.app import App
from kivy.logger import Logger
from kivy.metrics import inch

class _PersistentData(object):
    """
    base class for persistent data 
    """
    
    config_folder_name = 'my_configuration_files'

    def __init__(self, filename):
        self._id = -1
        """
        data_dir is where the data files must be stored.
        filename is the name of the file for pickling
        ex: 'my_settings.pickled'

        defaults is a dict or list.
        it contains the default settings.
        """
        default = self.data
        # app data
        self._appdata = App.get_running_app().appdata
        data_dir = self._appdata['user_data_dir']
        # create the file
        self.data_folder_path = os.path.join(data_dir, _PersistentData.config_folder_name)
        self._create_path(data_dir, filename)
        if filename is not None:
            if os.path.exists(self._path):
                f = open(self._path, 'rb')
                try:
                    self.data = pickle.load(f)
                except:
                    self.data = default
                f.close()
            else:
                # no such file
                self.data = default
        else:
            # no filename
            self._path = None
            self.data = default

    # path and folder

    @property
    def missing_configuration_folder_message(self):
        return 'Reinstall the stick or card with your "%s" folder.' % (_PersistentData.config_folder_name)
        
    def have_configuration_folder(self):
        return os.path.isdir(self.data_folder_path)
            
    def get_path(self):
        return self._path

    def _create_path(self, data_dir, filename):
        # create the folder path
        if self.have_configuration_folder() is False:
            os.mkdir(self.data_folder_path)
        if filename is not None:
            # there is one file for this persistent object
            # object can create multiple files (filename is None)
            self._path = os.path.join(self.data_folder_path, filename)

    # record

    @property
    def count(self):
        return len(self.data)

    def get(self, id):
        if id in self.data:
            return self.data[id]
        else:
            return None
            
    def set(self, ident, record):
        ok = ident in self.data
        if ok:
            self.data[ident] = record
        return (ok)

    def add(self, ident, record):
        ok = ident is None
        if ok:
            ident = self._new_record_id()
            self.data[ident] = record
        return (ok, ident)
    
    def remove(self, id):
        ok = id in self.data
        if ok:
            del(self.data[id])
        return ok
        
    def _new_record_id(self):
        if self._id == sys.maxint:
            self._id = -1
        self._id += 1
        while self._id in self.data:
            self._id += 1
        return self._id

    # file write

    def write(self):
        if self.have_configuration_folder() is False:
            return False, 'Unable to find your configuration folder path.'
        elif self._path is not None and self.data is not None:
            return self._write_pickle_file()
        else:
            return False, 'Nothing to save.'

    def _write_pickle_file(self):
        try:
            f = open(self._path, 'wb')
        except:
            return (False, 'Unable to open %s for writing.' % (self._path))
        try:
            pickle.dump(self.data, f, 2)
        except:
            try:
                os.unlink(self._path)
            except:
                pass
            return (False, 'Unable to write to %s.' % (self._path))
        f.close()
        return(True, 'Data Saved at %s' % (self._path))


class RangesModel(_PersistentData):
    def __init__(self, data_dir):
        self.data = {}
        _PersistentData.__init__(self,
                                'ranges.pickled')
                                
    @property
    def default_record(self):
        return {'fg':[1.0, 1.0, 1.0, 1.0], 'bg':[0.0, 0.0, 0.0, 1.0],
                'calibration':-1,
                'target':'',
                'yards':0, 'feet':0,
                'up':0, 'down':0,
                'description':''}

    def using_calibration(self, cal_ident):
        k = self.data.keys()
        for ident in k:
            if self.data[ident]['calibration'] == cal_ident:
                return True
        return False
        
    def get_ranges_using_calibration(self, cal_ident):
        l = []
        k = self.data.keys()
        for ident in k:
            if self.data[ident]['calibration'] == cal_ident:
                l.append(ident)
        return l
        
    def get_ident_using_description(self, description):
        k = self.data.keys()
        for ident in k:
            if self.data[ident]['description'] == description:
                return ident
        return None

    # backup and restore
        
    def restore(self, backup):
        if isinstance(backup, dict):
            self.data = backup
        
    def backup(self):
        return dict(self.data)


class CalibrationsModel(_PersistentData):
    def __init__(self, data_dir):
        self.data = {}
        _PersistentData.__init__(self,
                                'calibrations.pickled')
        if len(self.data) == 0:
            r = self.default_record
            r['description'] = 'Default'
            self.add(None, r)
            self.write()

    def get_ident_using_description(self, description):
        k = self.data.keys()
        for ident in k:
            if self.data[ident]['description'] == description:
                return ident
        return None

    def get_descriptions(self):
        l = []
        k = self.data.keys()
        for ident in k:
            l.append(self.data[ident]['description'])
        l.sort()
        return l
                                
    @property
    def default_record(self):
        pixels = inch(1)
        return {'pixels':pixels, 'description':''}

    # backup and restore
        
    def restore(self, backup):
        if isinstance(backup, dict):
            self.data = backup
        
    def backup(self):
        return dict(self.data)
